package drds.global_transaction.resource_manager.execute.dml;

import drds.global_transaction.resource_manager.ParameterObjectList;
import drds.global_transaction.resource_manager.execute.StatementExecuteTemplate;
import drds.global_transaction.resource_manager.model.Column;
import drds.global_transaction.resource_manager.model.Image;
import drds.global_transaction.resource_manager.model.TableMetaData;
import drds.global_transaction.resource_manager.proxy.resource_manager.StatementProxy;
import drds.global_transaction.resource_manager.sql_recognizer.SqlRecognizer;
import drds.global_transaction.resource_manager.sql_recognizer.UpdateSqlRecognizer;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class UpdateExecutor<T, S extends Statement> extends AbstractDmlExecutor<T, S>
{

	public UpdateExecutor(SqlRecognizer sqlRecognizer, StatementProxy statementProxy, StatementExecuteTemplate statementExecuteTemplate)
	{
		super(sqlRecognizer, statementProxy, statementExecuteTemplate);
	}

	@Override
	protected Image afterImage(Image beforeImage) throws SQLException
	{
		UpdateSqlRecognizer visitor = (UpdateSqlRecognizer) sqlRecognizer;
		TableMetaData tableMetaData = getTableMetaData();
		if (beforeImage == null || beforeImage.size() == 0)
		{
			return Image.empty(getTableMetaData());
		}
		List<String> updateColumnNameList = visitor.getUpdateColumnNameList();
		StringBuilder sb = new StringBuilder("select ");
		if (!tableMetaData.containsAllPrimaryKeyColumnNameList(updateColumnNameList))
		{
			// PK should be included.
			sb.append(tableMetaData.getFirstPrimaryKeyColumnName() + ", ");
		}
		int size = updateColumnNameList.size();
		for (int i = 0; i < size; i++)
		{
			sb.append(updateColumnNameList.get(i));
			if (i < (size - 1))
			{
				sb.append(", ");
			}
		}
		List<Column> primaryKeyColumnList = beforeImage.getPrimaryKeyColumnList();
		sb.append(" from " + tableMetaData.getTableName() + " where " + buildWhereConditionForPrimaryKeyColumnList(primaryKeyColumnList) + " for update");
		String sql = sb.toString();
		Image afterImage = null;
		PreparedStatement preparedStatement = null;
		ResultSet resultSet = null;
		try
		{
			preparedStatement = statementProxy.getConnection().prepareStatement(sql);
			int index = 0;
			for (Column column : primaryKeyColumnList)
			{
				index++;
				preparedStatement.setObject(index, column.getValue(), column.getType());
			}
			resultSet = preparedStatement.executeQuery();
			afterImage = Image.buildImage(tableMetaData, resultSet);

		} finally
		{
			if (resultSet != null)
			{
				resultSet.close();
			}
			if (preparedStatement != null)
			{
				preparedStatement.close();
			}
		}
		return afterImage;
	}

	@Override
	protected Image beforeImage() throws SQLException
	{
		UpdateSqlRecognizer updateSqlRecognizer = (UpdateSqlRecognizer) sqlRecognizer;
		TableMetaData tableMetaData = getTableMetaData();
		List<String> updateColumnNameList = updateSqlRecognizer.getUpdateColumnNameList();

		StringBuilder sb = new StringBuilder("select ");
		if (!tableMetaData.containsAllPrimaryKeyColumnNameList(updateColumnNameList))
		{
			// PK should be included.
			sb.append(tableMetaData.getFirstPrimaryKeyColumnName() + ", ");
		}
		int size = updateColumnNameList.size();
		for (int i = 0; i < size; i++)
		{
			sb.append(updateColumnNameList.get(i));
			if (i < (size - 1))
			{
				sb.append(", ");
			}
		}
		String whereCondition = null;
		ArrayList<Object> objectList = new ArrayList<>();
		if (statementProxy instanceof ParameterObjectList)
		{
			whereCondition = updateSqlRecognizer.getWhereCondition((ParameterObjectList) statementProxy, objectList);
		} else
		{
			whereCondition = updateSqlRecognizer.getWhereCondition();
		}
		sb.append(" from " + tableMetaData.getTableName() + " where " + whereCondition + " for update");
		String sql = sb.toString();
		Image beforeImage = null;
		Statement statement = null;
		ResultSet resultSet = null;
		try
		{
			if (objectList.isEmpty())
			{
				statement = statementProxy.getConnection().createStatement();
				resultSet = statement.executeQuery(sql);
			} else
			{
				PreparedStatement preparedStatement = statementProxy.getConnection().prepareStatement(sql);
				statement = preparedStatement;
				for (int i = 0; i < objectList.size(); i++)
				{
					preparedStatement.setObject(i + 1, objectList.get(i));
				}
				resultSet = preparedStatement.executeQuery();
			}
			beforeImage = Image.buildImage(tableMetaData, resultSet);

		} finally
		{
			if (resultSet != null)
			{
				resultSet.close();
			}
			if (statement != null)
			{
				statement.close();
			}
		}
		return beforeImage;
	}

}
